Unite 2018 | 解析AssetBundle
在日常的开发中,Unity的开发人员基本都离不开AssetBundle,而AssetBundle也是一个历史悠久的模块。在Unity的实际的企业技术支持过程中,还是有很多客户会遇到AssetBundle相关的一些问题。今天将分享Unity技术工程师刘伟贤在Unite Beijing 2018大会的演讲,帮助大家对AssetBundle有一个更深入的了解。
什么是AssetBundle
AssetBundle就像一个Zip压缩文件,里面存放着一些数据对象。它会包含一些平台相关的运行时序列化对象。
Bundle之间也存在着依赖。AssetBundle带有三种压缩选项:不压缩,LZMA , LZ4。默认的就是LZMA,而BuildAssetBundleOptions.ChunkBasedCompression就是LZ4的压缩形式。另外我们有二种类型的Bundle,一种是我们场景的Bundle(*.unity打包的产物),另一种是所谓松散的Bundle。
Asset Bundle Graph Tool
在日常的使用AssetBundle过程中,很多客户都说资源以及AB的管理都不好处理,都需要自己写各种导入代码或者后处理代码来进行资源的规范的统一及处理。
Assetbundle Graph Tool是为了帮助大家减少工作负担,主要针对在资源导入,打包AB和构建APP。通过使用这个工具,图形化的配置自己项目资源的Workflow。之前我们需要写一大堆自定义的代码来处理资源导入规格设置以及打包AB的策略,当需要调整的时候我们相当痛苦,但有了这个工具,一切的调整以及配置都变得相当简单。
Asset Bundle Browser Tool
其实在使用AssetBund的过程中,一不小心就会容易造成资源的重复打包。而Asset Bundle Browser Tool的优势就在于可以看到bundle内的关联资源,以及可以预见Bundle的大小,其基本原理就是去读取资源的AssetBundleName 。
WebExtract & Binary2Text
AssetBundle对于大家来说会是一个黑盒子,其实在Unity的安装目录下有WebExtract & Binary2Text这二个工具,可以帮你把AssetBundle这个黑盒子打开。例如:升级版本AssetBundle变大了,二次构建AssetBundle出现差异了,AssetBundle内到底包含了哪些资源等。
对于构建出来的AssetBundle,我们先通过WebExtract来解开,这时候可以得到一个文件夹,里面包含着一些文件。
AssetBundle的一些细节
场景的AssetBundle解开为BuildPlayer-<SceneName>和BuildPlayer-<SceneName>.sharedAssets。普通的AssetBundle解开为一个CAB-<GUIDString>的文件。BuildPlayer-<SceneName>和CAB-<GUIDString>对应的就是Profiler里面Others/SerializedFile里面的名字。
当调用WebExtract工具的时候,控制台还打印出来了一些信息。这里需要注意的是Size的组成。Bundle的Size是有header的信和blocks数据块、额外的一些数据Data组成。
Blocks根据不同的压缩方式会有不同的组织形式,譬如下图LZ4,它会产生三个压缩的Blocks,所以在读取资源的时候会先找到资源被压缩在哪个Blocks上,然后把Blocks解压并且Seek到对应位置去读对应的数据。而LZMA只有一个Block,需要把整个Blocks都解压后在读取对应的数据。
WebExtract解开的文件都是二进制文件,并不是明文,通过使用Binary2Text的工具可以把这些二进制文件直接反序列化成明文。
-detailed这个参数可以让序列化出来的文本带上更多详细的信息,包括这个资源占用了大小是多少,哪些大哪些小。
-hexfloat这个参数是把浮点数都以16进制的格式来输出,这样能够保证浮点精度的输出。我们曾经遇到过两次构建有差异的问题,通过WebExtract跟Binary2Text解开后发现文件还是一致的,但后面细查发现是因为float的输出的问题。所以加入了这个参数。
解开的文本文件大家可以一目了然的清楚知道里面的结构是如何,会包含哪些东西。
解开后文本内不同资源需要关注的一些点:
Assetbundle块:记录着当前AB的Assets,而Asset又会有PreloadIndex以及PreloadSzie来定义如何能把Asset给组织起来。
PreloadData块:当前AB的Assets的依赖的Asset资源。
External References块:引用外部的Assetbundle的列表,m_FileID & m_PathID: m_FileID为0表示资源在当前包内,不为0所以引用这外部的资源。其ID值对应着External Referecnes的列表。m_PathID为当前包内的唯一ID。
Material:可以确认其ShaderKeyword的数量是否是符合预期的,还可以看到ShaderProperty数值是否是正确的。
Texture:可以检查是否被重复打包了,其大小占用了多少。
Shader:可以检查是否含有了默认的Standard 或者额外的变体,通过SubProgram的数量来大致判断一下是否符合变体组合的数量。另外还可以有编译后的二进制大小。这些都直接影响到项目中ShaderLab的内存占用。
MonoScript: 我们脚本的关联,另外还会存有一些该脚本的一些数据。
Asset Bundle Inspector
通过Asset Bundle Inspector,我们可以更加直观显示已经加载的AssetBundle的内容。
SerializedFile
对于在Profiler内的Others/SerializedFile所占用的内存大小,很多客户都不知道这是些什么,如何去减少。
其实SerializedFile记录着重建资源所需的信息。而其大体的组成是有2 x 7KB的文件读取Buffer,较大的TypeTree占用,如果存有外部引用,会有一个最少72KB的External References的Buffer,剩下的就是我们资源的数据了,我们会在后续版本把External References的内存占用降到4KB+。
Asset Bundles的未来
我们的新的可寻址资产系统允许开发者通过一个地址就能在运行时取到对应的资源,不管这个资源是在本地,还是在WebServer上。到时大家再也不用为了资源的一大堆依赖,一大堆接口,Editor和Runtime的加载接口不一致,生命周期管理等等一系列问题而烦恼,让我们一起期待吧!
更多Unite Beijing 2018技术演讲分享,尽在Unity官方中文论坛(Unitychina.cn)!
推荐阅读
点击“阅读原文”访问Unity官方中文论坛